<template>
  <el-dialog
    :title="$t('pictureManager.upload')"
    :close-on-click-modal="false"
    :visible.sync="visible"
    top="200px"
    v-if="visible"
    width="770px"
    @close="handleDialogClose"
    append-to-body
  >
    <div class="elx-imgbox-dialog">
      <div class="elx-main">
        <el-form label-width="auto">
          <el-form-item :label="$t('resource.selectGroup')+':'">
            <el-select size="small" v-model="selectGroup" clearable :placeholder="$t('tip.select')"
                       class="select-group-box-item">
              <el-option
                v-for="item in groupList"
                :key="item.attachFileGroupId"
                :label="item.name"
                :value="item.attachFileGroupId">
              </el-option>
            </el-select>
            <div class="default-btn text-btn select-group-box-item" @click="createGroup">{{$t('resource.newGroup')}}
            </div>
          </el-form-item>
          <el-form-item :label="$t('pictureManager.upload')+':'">
            <el-upload
              ref="upload"
              class="upload-demo"
              multiple
              :limit="1"
              :show-file-list="false"
              action="#"
              v-loading.lock="loading"
              element-loading-text="上传中"
              element-loading-spinner="el-icon-loading"
              element-loading-background="rgba(0, 0, 0, 0.01)"
              :before-upload="beforeup"
              :http-request="beforeAvatarUpload">
              <el-input v-model="param.fileName" :placeholder="$t('text.upload')" readonly/>
            </el-upload>
          </el-form-item>
        </el-form>
      </div>
    </div>
    <span slot="footer">
      <div class="default-btn" @click="handleDialogClose">{{$t('shopFeature.edit.back')}}</div>
      <div
        class="primary-btn default-btn"
        @click="onUploadConfirm"
      >{{$t('pictureManager.confirmUpload')}}</div>
    </span>
    <group-add-or-update ref="groupAddOrUpdate" v-if="groupVisible" @getGroupData="getGroupList"
                         @pageUpdateGroup="pageUpdateGroup"/>
  </el-dialog>
</template>

<script type="text/babel">
  // import { Debounce } from '@/utils/debounce'
  import groupAddOrUpdate from '@/components/group-add-or-update'
  import imageCompression from 'browser-image-compression'
  import SparkMD5 from 'spark-md5'
  // 分片大小 5-10m
  const chunkSize = 10 * 1024 * 1024
  export default {
    name: 'uploadPic',
    components: {
      groupAddOrUpdate
    },
    data () {
      return {
        allurl: null,
        loading: false,
        progress: 0,
        param: {
          fileName: '',
          fileType: '',
          filePath: '',
          fileSize: '',
          type: 4
        },
        status: true,
        progressShow: false,
        lang: localStorage.getItem('lang') || 'zh_CN',
        fileIds: [], // 已选图片的fileId
        dataForm: {},
        uploadFileNum: 0,
        uploadDisabled: false,
        uploadSubmitFlag: false,
        createGroupName: '', // 新建分组名
        groupVisible: false,
        showBatchMove: false,
        options: {
          multiple: true, // 是否支持选取多个图片
          limit: 20, // 最多可选择图片数量
          maxSize: 2, // 最大尺寸（M）
          enableUpload: true, // 是否启用图片上传
          callback: null
        },
        groupList: [], // 分组列表
        selectGroup: '',
        showGroupType: 0, // 0：新建分组 1：修改分组
        newImgName: '',
        isTrue: true,
        errShow: false,
        hideUploadEdit: false, // 上传图片按钮显隐
        picNum: 0,
        resourcesUrl: process.env.VUE_APP_RESOURCES_URL,
        type: 4,
        fileName: '',
        listType: 'picture',
        isLoading: true,
        visible: false,
        notImg: false,
        uploadSuccessCount: 0,
        imgRes: {
          current: 1
        },
        selectAll: false,
        uploadFlag: 0,
        groupId: 0, // 点击分组
        updateGroupId: 0 // 修改分组的id
      }
    },
    mounted () {

      // console.log(this.$refs.upload)
    },
    methods: {
      closeMain (obj) {
        this.dataForm.filePath = obj.filePath
        this.dataForm.fileSize = obj.fileSize
      },
      show () {
        this.selectGroup = ''
        this.getGroupList()
        this.visible = true
      },

      hide () {
        this.visible = false
        // console.log(12312)
      },
      /**
       * 批量移动分组
       */
      batchMove () {
        if (!this.fileIds.length) {
          this.$message({
            message: this.$i18n.t('pictureManager.tips1'),
            type: 'error',
            duration: 1000
          })
          return
        }
        this.showBatchMove = true

        // console.log(this.fileIds)
      },
      submitBatchMove () {
        if (!this.selectGroup) {
          this.visible = false
          this.$emit('getImgListData')
          return this.$message({
            type: 'success',
            message:  this.$i18n.t('resource.uploadSuccess')
          })
        }
        this.$http({
          url: this.$http.adornUrl('/admin/file/batchMove'),
          method: 'put',
          data: this.$http.adornData({
            fileIds: this.fileIds.map(x => x.data),
            attachFileGroupId: this.selectGroup
          })
        }).then(({data}) => {
          this.fileIds = []
          this.$message({
            type: 'success',
            message: this.$i18n.t('pictureManager.pic') + this.$i18n.t('resource.uploadSuccess')
          })
          this.$emit('getImgListData')
          this.visible = false
        })
      },
      pageUpdateGroup () {
        this.$emit('getGroupData')
      },
      /**
       * 获取分组列表
       */
      getGroupList () {
        this.$http({
          url: this.$http.adornUrl('/admin/fileGroup/list'),
          method: 'get',
          params: {
            type: 4 // 1、图片 2、视频 3、文件
          }
        }).then(res => {
          this.groupList = res.data
        })
      },
      /**
       * 点击分组
       */
      clickGroup (id) {
        this.groupId = id
      },
      updateGroup (id, name) {
        this.updateGroupId = id
        this.createGroupName = name
        this.showGroupType = 1
        this.groupVisible = true
      },
      createGroup () {
        this.groupVisible = true
        this.$nextTick(() => {
          this.$refs.groupAddOrUpdate.show(4)
        })
      },
      deleteGroup (id) {
        this.$confirm(this.$i18n.t('pictureManager.tips2'), this.$i18n.t('resource.tips'), {
          confirmButtonText: this.$i18n.t('resource.confirm'),
          cancelButtonText: this.$i18n.t('resource.cancel'),
          type: 'warning'
        }).then(() => {
          this.$http({
            url: this.$http.adornUrl('/admin/fileGroup'),
            method: 'delete',
            params: {
              attachFileGroupId: id
            }
          }).then(res => {
            this.getGroupList()
          })
        })
      },
      /**
       * 提交上传图片
       */
      onUploadConfirm () {
        this.uploadSubmitFlag = true
        var time = setTimeout(() => {
          this.uploadSubmitFlag = false
          clearTimeout(time)
        }, 1000)
        this.uploadFlag = 0
        this.fileIds = []
        this.notImg = true
        if (!this.param.fileName || !this.param.fileSize) {
          return this.$message.error(this.$i18n.t('pictureManager.tips4'))
        }
        // this.$refs.upload.submit()
        this.saveFile(this.param)
      },

      onUploadProgress (event, file, fileList) {

      },
      beforeRemove (file, fileList) {
        // 防止用户频繁点击导致删除过多
        if (this.uploadDisabled) {
          return false
        } else {
          return true
        }
      },
      onRemove (file, fileList) {
        this.uploadFileNum = this.$refs.upload.uploadFiles.length
        var time = setTimeout(() => {
          this.hideUploadEdit = false
          clearTimeout(time)
        }, 1000)
        if (this.uploadSubmitFlag) {
          this.uploadDisabled = false
        } else {
          this.uploadDisabled = true
          var time1 = setTimeout(() => {
            this.uploadDisabled = false
            clearTimeout(time1)
          }, 1000)
        }
      },
      onUploadChange (file, fileList) {
        this.uploadFileNum = this.$refs.upload.uploadFiles.length
        if (this.$refs.upload.uploadFiles.length >= this.options.limit) {
          this.hideUploadEdit = true
        } else {
          this.hideUploadEdit = false
        }
      },
      errMsg () {
        if (this.errShow && this.uploadFileNum === this.picNum) {
          this.picNum = 0
          this.message(this.$i18n.t('pictureManager.onlyPictures'), true)
          this.notImg = false
          this.errShow = false
        }
      },
      /**
       * 上传图片前检查合法性
       * @param file
       * @returns {boolean}
       */
      async beforeUpload (file) {
        this.picNum++
        let typeArray = file.type.split('/')
        this.errMsg()
        if (typeArray[0] !== 'image') {
          this.errShow = true
          this.errMsg()
          return Promise().reject()
        }
        if (this.uploadFileNum === this.picNum) {
          this.picNum = 0
        }
        const isSize = file.size / (1024 * 1024) < 2
        if (!isSize) {
          try {
            const resultBlob = await imageCompression(file, {
              maxSizeMB: 2 // 压缩大小
            })
            const fs = new File([resultBlob], file.name, {
              type: file.type
            })
            return fs
          } catch (error) {
            return Promise().reject()
          }
          // this.errShow = true
          // this.errMsg()
          // return false
        }
        return true
      },

      uploadNumberLimit () {
        if (!this.options.multiple) {
          return 1
        }
        return this.options.limit - this.uploadFileNum
      },

      uploadTypeTip () {
        return this.$i18n.t('pictureManager.onlySupported') + ' jpg/png/gif ' + this.$i18n.t('pictureManager.pic')
      },

      uploadSizeTip () {
        return this.$i18n.t('pictureManager.notExceed') + '2M'
      },

      uploadTips () {
        let tips = [this.uploadTypeTip(), this.uploadSizeTip()]

        if (!this.options.multiple) {
          return tips.join('，')
        }
        if (this.uploadFileNum > 0) {
          tips.push(this.$i18n.t('pictureManager.soonUpload') + this.uploadFileNum + this.$i18n.t('pictureManager.unit'))
        }
        tips.push(this.$i18n.t('pictureManager.remainder') + (this.options.limit - this.uploadFileNum) + this.$i18n.t('pictureManager.unit') + this.$i18n.t('pictureManager.upload'))

        return tips.join(',')
      },

      /**
       * 上传错误处理
       * @param err
       * @param file
       * @param fileList
       */
      onUploadError (err, file, fileList) {
        this.message(this.$i18n.t('pictureManager.requestError'), true)
        // console.log(err)
        throw err
      },

      /**
       * 上传成功处理
       * @param response
       * @param file
       * @param fileList
       * @returns {boolean}
       */
      onUploadSuccess (response, file, fileList) {
        this.uploadFlag++
        this.fileIds.push(response)
        // this.disabled = true
        if (this.uploadFileNum === this.uploadFlag) {
          this.submitBatchMove()
          this.$refs.upload.uploadFiles = []
        }
      },

      /**
       * 选择上传文件超过限制文件个数提示
       */
      onUploadExceedTip () {
        this.message(this.$i18n.t('pictureManager.maxSelect') + this.uploadNumberLimit() + this.$i18n.t('pictureManager.unit') + this.$i18n.t('pictureManager.upload'))
      },
      message (msg, isInfo) {
        var type = 'error'
        if (isInfo) {
          type = 'info'
        }
        this.$message({
          message: msg,
          type: type,
          duration: 1500
        })
      },
      /**
       * 关闭回调
       */
      handleDialogClose () {
        this.$emit('handleDialogClose')
        this.fileName = ''
        this.visible = false
        this.fileIds = []
      },

      beforeAvatarUpload (data) {
        this.uploadFile(data.file)
      },

      /**
       * 注意：本测试Demo不受分片顺序影响
       * 关于上传文件成功后的处理：配置minio监听指定存储桶指定格式文件上传成功后，push通知到mq,后端程序监听并消费即可
       * （建议上传mp4，成功后可以直接在页面看到效果）
       * 测试分片上传
       *      运行页面 > 打开控制台 > console > 选择上传的文件 > 观察打印的信息
       * 测试秒传
       *      在上一个测试的基础上，刷新一下页面，选择上一次上传的文件
       * 测试断点续传
       *      重新选择一个文件(如果你没有多的测试文件，就重启一下后台服务) > 手动模拟上传了一部分失败的场景(在所有分片未上传完成时关掉页面 或 注释掉合并文件代码，然后去 minio chunk桶 删除几个分片)
       *      > 再选择刚选择的文件上传 > 观察打印的信息是否从缺失的分片开始上传
       */
      beforeup () {
        this.$refs['upload'].clearFiles()
      },
      async uploadFile (file) {

        this.progressShow = true
        this.progress = 0
        this.loading = true
        // 文件大小(大于5m再分片哦，否则直接走普通文件上传的逻辑就可以了，这里只实现分片上传逻辑)
        const fileSize = file.size

        if (fileSize <= chunkSize) {
          // 小文件上传
          const initUploadParams = JSON.stringify({bucketName: 'mall4j', fileName: file.name})
          this.$http({
            url: this.$http.adornUrl('/ua/upload/init-chunk-upload-min'),
            method: 'post',
            headers: {
              'Content-Type': 'application/json'
            },
            data: initUploadParams
          }).then(async (res) => {
            let chunkUploadUrl = res.data.data
            let url = this.getUrlRelativePath(chunkUploadUrl)
            this.$http({
              url: chunkUploadUrl,
              method: 'put',
              data: file.slice(0, fileSize),
              headers: {
                'Content-Type': file.type
              },
            }).then(res => {
              this.progress = 100
              this.loading = false
              this.$message('上传完成！')
              // 保存文件上传记录
              this.param.fileName = file.name
              this.param.fileType = file.name.split('.')[1]
              this.param.filePath = url
              this.param.fileSize = Math.floor(fileSize / 1024 * 100) / 100
              this.progressShow = false
            }).catch(error => {
              this.progress = 0
              this.loading = false
              this.$message('上传失败，进度为：0%')
            })
          }).catch(error => {
            this.loading = false
            this.$message('上传接口异常')
          })
          return
        }

        // 计算当前选择文件需要的分片数量
        let chunkCount = Math.ceil(fileSize / chunkSize)
        if (chunkCount > 1 && fileSize % chunkSize != 0) {
          chunkCount--
        }
        console.log('文件大小：', (file.size / 1024 / 1024) + 'Mb', '分片数：', chunkCount)

        //获取文件md5
        const fileMd5 = await this.getFileMd5(file)
        console.log('文件md5：', fileMd5)

        console.log('向后端请求本次分片上传初始化')
        //向后端请求本次分片上传初始化
        const initUploadParams = JSON.stringify({bucketName: 'mall4j', chunkCount: chunkCount, fileMd5: fileMd5})

        this.$http({
          url: this.$http.adornUrl('/ua/upload/init-chunk-upload'),
          method: 'post',
          headers: {
            'Content-Type': 'application/json'
          },
          data: initUploadParams
        }).then(async (res) => {
          res = res.data
          //code = 0 文件在之前已经上传完成，直接走秒传逻辑；code = 1 文件上传过，但未完成，走续传逻辑;code = 200 则仅需要合并文件
          if (res.code === 200) {
            this.progress = 100
            console.log('当前文件上传情况：所有分片已在之前上传完成，仅需合并')
            this.composeFile(fileMd5, file.name, fileSize, file.type)
            return
          }
          if (res.code === 0) {
            let url = this.getUrlRelativePath(res.data.filePath)
            console.log('当前文件上传情况：秒传')
            this.loading = false
            this.progress = 100
            this.$message('上传成功！')
            this.param.fileName = file.name
            this.param.fileType = file.name.split('.')[1]
            this.param.filePath = url
            this.param.fileSize = Math.floor(fileSize / 1024 * 100) / 100
            this.progressShow = false
            // this.$emit('input', res.data.filePath.split("?")[0])
            this.$emit('closeMain', {'filePath': url, 'fileSize': Math.floor(fileSize / 1024 * 100) / 100})
            return
          }
          const chunkUploadUrls = res.data
          if (chunkCount - chunkUploadUrls.length > 0) {
            console.log('当前文件上传情况：断点续传')
          } else {
            console.log('当前文件上传情况：初次上传')
          }
          this.progress = parseInt(((chunkCount - chunkUploadUrls.length) / chunkCount) * 100)
          //当前为顺序上传方式，若要测试并发上传，请将第52行 await 修饰符删除即可
          //若使用并发上传方式，当前分片上传完成后打印出来的完成提示是不准确的，但这并不影响最终运行结果；原因是由ajax请求本身是异步导致的
          let count = 0
          let counterror = 0
          let index = 0
          for (let item of chunkUploadUrls) {
            if (!this.status) {
              return true
            }
            index++
            //如果超过10个碎片正在上传就设置等待时间
            if (index - count - counterror > 5) {
              await this.resolveAfter2Seconds(index - count - counterror)
            }

            //分片开始位置
            let start = (item.partNumber - 1) * chunkSize
            let end = 0
            if (chunkCount == item.partNumber) {
              end = fileSize
            } else {
              //分片结束位置fileSize % chunkSize
              end = start + chunkSize
            }
            //取文件指定范围内的byte，从而得到分片数据
            let _chunkFile = file.slice(start, end)
            console.log('开始上传第' + item.partNumber + '个分片')
            this.$http({
              url: item.uploadUrl,
              method: 'put',
              data: _chunkFile,
              headers: {
                'Content-Type': file.type
              },
            }).then(res => {
              count++
              console.log('第' + item.partNumber + '个分片上传完成，本次上传成功数：' + count)
              this.progress = parseInt(((chunkCount - chunkUploadUrls.length + count) / chunkCount) * 100)
              if (count == chunkUploadUrls.length) {
                //请求后端合并文件
                this.composeFile(fileMd5, file.name, fileSize, file.type)
              }
              if (counterror + count == chunkUploadUrls.length && count < chunkUploadUrls.length) {
                this.loading = false
                this.$message('上传失败，进度为：' + parseInt(((chunkCount - chunkUploadUrls.length + count) / chunkCount) * 100) + '%')
              }
            }).catch(error => {
              counterror++
              console.log(error + '失败数量' + counterror)
              if (counterror + count == chunkUploadUrls.length) {
                this.loading = false
                this.$message('上传失败，进度为：' + parseInt(((chunkCount - chunkUploadUrls.length + count) / chunkCount) * 100) + '%')
              }
            })
          }

        }).catch(error => {
          this.loading = false
          this.$message('上传接口异常')
        })
      },
      resolveAfter2Seconds (ing) {
        return new Promise(resolve => {
          //等待规律 剩余10个下不用等待
          let time = ing / 5 * 1500
          console.log(ing + '个碎片正在上传中，等待' + time / 1000 + '秒后发起下批上传')
          setTimeout(() => {
            resolve('resolved')
          }, time)
        })
      },
      /**
       * 请求后端合并文件
       * @param fileMd5
       * @param fileName
       */
      composeFile (fileMd5, fileName, fileSize, fileType) {
        console.log('开始请求后端合并文件')
        //注意：bucketName请填写你自己的存储桶名称，如果没有，就先创建一个写在这
        const composeParams = JSON.stringify({
          fileMd5: fileMd5,
          fileName: fileName,
          bucketName: 'mall4j',
          contentType: fileType
        })
        this.$http({
          url: this.$http.adornUrl('/ua/upload/compose-file'),
          method: 'post',
          headers: {
            'Content-Type': 'application/json'
          },
          data: composeParams
        }).then((res) => {
          console.log('合并文件完成', res)
          // this.$parent.dataForm.filePath = res.filePath.split("?")[0]
          this.loading = false
          this.$message('上传完成！')
          //  this.$emit('input', res.filePath.split("?")[0])
          let url = this.getUrlRelativePath(res.data.filePath)

          this.param.fileName = fileName
          this.param.fileType = fileType
          this.param.filePath = url
          this.param.fileSize = fileSize
          // this.$emit('closeMain', {'filePath': url, 'fileSize': Math.floor(fileSize / 1024 * 100) / 100})
          this.progressShow = false
        }).catch(error => {
          this.loading = false
          this.$message('合成分片失败：请再试')
        })
      },
      getUrlRelativePath (url) {
        var relUrl = url.replace(this.resourcesUrl, '')//stop省略，截取从start开始到结尾的所有字符
        if (relUrl.indexOf('?') != -1) {
          relUrl = relUrl.split('?')[0]
        }
        return relUrl
      },
      /**
       * 获取文件MD5
       * @param file
       * @returns {Promise<unknown>}
       */
      getFileMd5 (file) {
        let fileReader = new FileReader()
        fileReader.readAsBinaryString(file)
        // console.log(SparkMD5())
        let spark = new SparkMD5()
        console.log(spark)
        return new Promise((resolve) => {
          fileReader.onload = (e) => {
            spark.appendBinary(e.target.result)
            resolve(spark.end())
          }
        })
      },
      saveFile (data) {
        // 获取文件名称后缀
        var fileNames = data.fileName.split('.')
        if(fileNames.length > 1){
          data.fileType = fileNames[1]
        }
        this.$http({
          url: this.$http.adornUrl('/admin/file/saveFile'),
          method: 'post',
          data: data
        }).then(res => {
          // this.$emit('getImgListData')
          this.fileIds.push(res)
          this.visible = false
          this.submitBatchMove()
          this.param = {
            fileName: '',
            fileType: '',
            filePath: '',
            fileSize: '',
            type: 4
          }
          this.$emit('searchImg')
        })
      },
      refreshPic (imagePath) {
        this.$emit('input', imagePath)
      }
    }
  }
</script>

<style lang="scss" scoped>
  .elx-imgbox-dialog ::v-deep {
    .el-upload-list__item,
    .el-upload--picture-card {
      width: 78px !important;
      height: 78px !important;
      line-height: 88px;
      border-radius: 5px;
    }
  }

  .disUoloadSty ::v-deep .el-upload--picture-card {
    display: none; /* 上传按钮隐藏 */
  }

  ::v-deep .el-upload-list--picture-card .el-upload-list__item-status-label {
    top: -5px;
  }

  ::v-deep .el-upload-list--picture-card .el-upload-list__item-status-label i {
    vertical-align: top;
  }
</style>
<style lang="scss" scoped>
  .select-group-box-item {
    margin-right: 10px;
  }

  div > > > .el-tabs__header {
    display: none !important;
  }

  .upload-tip {
    font-size: 12px;
    color: #999999;
  }
</style>
<style scoped>
  .hide >>> .el-upload--picture-card {
    display: none;
  }
</style>


